///PHILOTES Source Code.  (C)2012 PhiloLabs
#pragma once

#ifndef __RenderQueue_H__
#define __RenderQueue_H__

#include "OgreHeaderPrefix.h"
#include "OgrePrerequisites.h"
#include "OgreIteratorWrappers.h"

namespace Ogre {

	class Camera;
	class MovableObject;
	struct VisibleObjectsBoundsInfo;

	
	
	
    enum RenderQueueGroupID
    {
        /// Use this queue for objects which must be rendered first e.g. backgrounds
        RENDER_QUEUE_BACKGROUND = 0,
        /// First queue (after backgrounds), used for skyboxes if rendered first
        RENDER_QUEUE_SKIES_EARLY = 5,
        RENDER_QUEUE_1 = 10,
        RENDER_QUEUE_2 = 20,
		RENDER_QUEUE_WORLD_GEOMETRY_1 = 25,
        RENDER_QUEUE_3 = 30,
        RENDER_QUEUE_4 = 40,
		/// The default render queue
        RENDER_QUEUE_MAIN = 50,
        RENDER_QUEUE_6 = 60,
        RENDER_QUEUE_7 = 70,
		RENDER_QUEUE_WORLD_GEOMETRY_2 = 75,
        RENDER_QUEUE_8 = 80,
        RENDER_QUEUE_9 = 90,
        /// Penultimate queue(before overlays), used for skyboxes if rendered last
        RENDER_QUEUE_SKIES_LATE = 95,
        /// Use this queue for objects which must be rendered last e.g. overlays
        RENDER_QUEUE_OVERLAY = 100, 
		/// Final possible render queue, don't exceed this
		RENDER_QUEUE_MAX = 105
    };

    #define OGRE_RENDERABLE_DEFAULT_PRIORITY  100

    
    class _OgreExport RenderQueue : public MemAlloc_SceneCtrl
    {
    public:

		class RenderQueueGroupMap
		{
		public:
			class value_type
			{
			public:
				value_type(uint8 _f, RenderQueueGroup* _g) : first(_f), second(_g)
				{

				}
				value_type() : first(0), second(0)
				{

				}
				uint8 first;
				RenderQueueGroup* second;
			};

			typedef uint8 key_type;
			typedef RenderQueueGroup* mapped_type;
			typedef value_type& reference;
			typedef value_type* pointer;
			typedef const value_type& const_reference;
			typedef const value_type* const_pointer;
			typedef vector<value_type >::type GroupVector;

			class iterator
			{
				friend class RenderQueueGroupMap;
			public:

				iterator() : mRenderQueueGroupMap(0), mIndex(RENDER_QUEUE_MAX)
				{
				}

				iterator(const RenderQueueGroupMap* _mRenderQueueGroupMap, uint8 _index) : mRenderQueueGroupMap(_mRenderQueueGroupMap), mIndex(_index)
				{
				}

				const_reference operator*() const
				{	
					assert(this->mRenderQueueGroupMap);

					return mRenderQueueGroupMap->mGroupVector[mIndex];
				}

				const_pointer operator->() const
				{
					assert(this->mRenderQueueGroupMap);

					return &(mRenderQueueGroupMap->mGroupVector[mIndex]);
				}

				iterator& operator++()
				{
					assert(this->mRenderQueueGroupMap);

					while(mIndex < mRenderQueueGroupMap->mMaxID)
					{
						++mIndex;
						if(mRenderQueueGroupMap->mGroupVector[mIndex].second != 0)
						{
							break;
						}
					}
					return (*this);
				}

				const iterator& operator++()const
				{
					assert(this->mRenderQueueGroupMap);

					while(mIndex < mRenderQueueGroupMap->mMaxID)
					{
						++mIndex;
						if(mRenderQueueGroupMap->mGroupVector[mIndex].second != 0)
						{
							break;
						}
					}
					return (*this);
				}

				iterator operator++(int)
				{
					assert(this->mRenderQueueGroupMap);

					iterator temp = *this;
					while(mIndex < mRenderQueueGroupMap->mMaxID)
					{
						++mIndex;
						if(mRenderQueueGroupMap->mGroupVector[mIndex].second != 0)
						{
							break;
						}
					}
					return (temp);
				}

				const iterator operator++(int)const
				{
					assert(this->mRenderQueueGroupMap);

					const_iterator temp = *this;
					while(mIndex < mRenderQueueGroupMap->mMaxID)
					{
						++mIndex;
						if(mRenderQueueGroupMap->mGroupVector[mIndex].second != 0)
						{
							break;
						}
					}
					return (temp);
				}

				bool operator !=(const iterator& o)const
				{
					assert(mRenderQueueGroupMap);
					assert(o.mRenderQueueGroupMap);
					assert(o.mRenderQueueGroupMap == this->mRenderQueueGroupMap);
					if( o.mIndex != this->mIndex)
						return true;
					return false;
				}

				bool operator ==(const iterator& o)const
				{
					assert(this->mRenderQueueGroupMap);
					assert(o.mRenderQueueGroupMap);
					assert(o.mRenderQueueGroupMap == this->mRenderQueueGroupMap);
					if(o.mIndex == this->mIndex)
					{
						return true;
					}
					return false;
				}
			protected:
				const RenderQueueGroupMap* mRenderQueueGroupMap;
				mutable uint8 mIndex;
			};

			typedef const iterator const_iterator;
			
			RenderQueueGroupMap() : mMinID(RENDER_QUEUE_MAX), mMaxID(RENDER_QUEUE_MAX)
			{
				mGroupVector.resize(RENDER_QUEUE_MAX + 2);
			}

			void insert(value_type v)
			{
				mGroupVector[v.first] = v;
				if(v.first < mMinID)
				{
					mMinID = v.first;
				}

				if(v.first >= mMaxID || mMaxID == RENDER_QUEUE_MAX)
				{
					mMaxID = v.first + 1;
				}
			}

			iterator find(uint8 key)
			{
				if(mGroupVector[key].second == 0)
				{
					return iterator(this, mMaxID);
				}
				else
				{
					return iterator(this, key);
				}
			}

			iterator begin()
			{
				return iterator(this, mMinID);
			}

			iterator end()
			{
				return iterator(this, mMaxID);
			}

			const_iterator begin() const
			{
				return iterator(this, mMinID);
			}

			const_iterator end() const
			{
				return iterator(this, mMaxID);
			}
			
			void clear()
			{
				mGroupVector.reserve(RENDER_QUEUE_MAX + 2);
				mGroupVector.clear();
				mGroupVector.resize(RENDER_QUEUE_MAX + 2);
				mMinID = RENDER_QUEUE_MAX;
				mMaxID = RENDER_QUEUE_MAX;
			}
		protected:
			uint8 mMinID;
			uint8 mMaxID;
			GroupVector mGroupVector;
		};

		typedef MapIterator<RenderQueueGroupMap> QueueGroupIterator;
		typedef ConstMapIterator<RenderQueueGroupMap> ConstQueueGroupIterator;

		
		class _OgreExport RenderableListener
		{
		public:
			RenderableListener() {}
			virtual ~RenderableListener() {}

			
			virtual bool renderableQueued(Renderable* rend, uint8 groupID, 
				ushort priority, Technique** ppTech, RenderQueue* pQueue) = 0;
		};
    protected:
        RenderQueueGroupMap mGroups;
        /// The current default queue group
        uint8 mDefaultQueueGroup;
        /// The default priority
        ushort mDefaultRenderablePriority;

        bool mSplitPassesByLightingType;
        bool mSplitNoShadowPasses;
		bool mShadowCastersCannotBeReceivers;

		RenderableListener* mRenderableListener;
    public:
        RenderQueue();
        virtual ~RenderQueue();

        
        void clear(bool destroyPassMaps = false);

		
		RenderQueueGroup* getQueueGroup(uint8 qid);

        
        void addRenderable(Renderable* pRend, uint8 groupID, ushort priority);

        
        void addRenderable(Renderable* pRend, uint8 groupId);

        
        void addRenderable(Renderable* pRend);
        
        
        uint8 getDefaultQueueGroup(void) const;

        
        void setDefaultRenderablePriority(ushort priority);

        
        ushort getDefaultRenderablePriority(void) const;

        
        void setDefaultQueueGroup(uint8 grp);
        
        
        QueueGroupIterator _getQueueGroupIterator(void);
        ConstQueueGroupIterator _getQueueGroupIterator(void) const;

        
        void setSplitPassesByLightingType(bool split);

        
        bool getSplitPassesByLightingType(void) const;

        
        void setSplitNoShadowPasses(bool split);

        
        bool getSplitNoShadowPasses(void) const;

		
		void setShadowCastersCannotBeReceivers(bool ind);

		
		bool getShadowCastersCannotBeReceivers(void) const;

		
		void setRenderableListener(RenderableListener* listener)
		{ mRenderableListener = listener; }

		RenderableListener* getRenderableListener(void) const
		{ return mRenderableListener; }

		
		void merge( const RenderQueue* rhs );
		
		void processVisibleObject(MovableObject* mo, 
			Camera* cam, 
			bool onlyShadowCasters, 
			VisibleObjectsBoundsInfo* visibleBounds);

    };

	
	

}

#include "OgreHeaderSuffix.h"
#endif
